#ifndef _ACE_MESH_RUNTIME_
#define _ACE_MESH_RUNTIME_


#ifdef __cplusplus
extern "C" {
#endif

#include <stdio.h>
#include <string.h>
// #include "aceMesh_task.h"
#include "am_machine.h"

#ifdef MEMORY_POOL
#include "MemPool.h"
#endif
#ifdef THREAD_TRACE
#include "thread_trace.h"
#endif
#ifdef  NO_PARALLEL
#ifdef CONCURRENT_CONSTRUCT_GRAPH
	#include "aceMesh_concurrent_task.h"
#else
	#include "aceMesh_task.h"
#endif
#else

#endif

#include "task.h"
typedef int Error_Code;
#define ACEMESH_OK  0


#if defined(DYNAMIC_SCHEDULER)
#define ACEMESH_NEW  new ( tbb::task::allocate_root() )
#elif defined(ACEMESH_SCHEDULER)
#define ACEMESH_NEW  new 
#endif

#define MAX_NEIGHBORS 27
//#define MAX_NEIGHBORS 100
#define LDM_NEIGHBORS 16
#define mymin(a,b) ((a<b)?(a):(b))

#ifdef __ACEMESH_THREAD_GROUP
// interfaces for hierarchical execution
Error_Code aceMesh_runtime_init(int total_threads, int group_size, int core_ids[]);
Error_Code aceMesh_runtime_init(int total_threads, int group_size);
Error_Code aceMesh_runtime_init(int total_threads);
Error_Code aceMesh_runtime_init(int total_threads, int core_ids[]);
Error_Code aceMesh_runtime_init();
void set_splitter_dim_chunks( size_t dim, ... );
#include "range.h"

typedef AceMesh_runtime::range_d range_d;
#endif
Error_Code aceMesh_runtime_init_TNum(int thread_num);
Error_Code aceMesh_runtime_init(int thread_num, int core_ids[]);  
Error_Code aceMesh_runtime_init_zeroPara();
Error_Code aceMesh_runtime_shutdown();
Error_Code aceMesh_runtime_shutdown_with_proc_id(int proc_id);

//NEIGHBOR relationship
#define NEIGHBOR_NONE      0x00000000
#define NEIGHBOR_UP        0x00000001
#define NEIGHBOR_DOWN      0x00000002
#define NEIGHBOR_LEFT      0x00000004
#define NEIGHBOR_RIGHT     0x00000008
#define NEIGHBOR_FRONT     0x00000010
#define NEIGHBOR_BACK      0x00000020
#define NEIGHBOR_ALL       0x0000ffff

//the read/write flag for addr
#define INOUT_NONE 0
#define IN         1
#define OUT        2
#define INOUT      3

//the type flag for the area that stands for by addr
#define NORMAL               1 
#define SHADE                2
#define UNSHADE              3
#define SHADE_AND_UNSHADE    4


//for task type
// typedef int task_type;
#define NOT_SET 0
#define STENCIL_TASK    1
#define NOAFFINITY_TASK 2
#define BLOCKING_TASK 3   //for mpi_wait, or mpi_send,mpi_recv etc blocking ops.
#define COMPOSITE_TASK 4
#define COMPOSITE_END_TASK 5
//end

#define RTS_undefined_event "21"
#define RTS_event_end "00"


//n void* addr, short int area, int neighbor, int inout_type, int neighbor_inout_type
//eg: A[0], NORMAL, NEIGHBOR_ALL, INOUT, IN
//eg  B[0], SHADE_AND_UNSHADE, NEIGHBOR_NONE, INOUT, INOUT_NONE, (SHADE)
//                           NEIGHBOR_ALL,  INOUT_NONE, IN   , (UNSHADE)
//eg  C[0], SHADE, NEIGHBOR_NONE, INOUT, INOUT_NONE, (SHADE)
//eg  D[0], UNSHADE, NEIGHBOR_NONE, INOUT, INOUT_NONE, (UNSHADE)
Error_Code register_task(struct aceMesh_task* t, int n, ...);
Error_Code register_task_datainfo(struct aceMesh_task* t, int n, double* ad,int area,int nei,int ty,int ne); //(aceMesh_task* t, int n, ...);
Error_Code register_task_end(struct aceMesh_task* t);


#ifdef __ACEMESH_THREAD_GROUP
// fine-grain task interface.
template<typename TaskBody, typename RangeType>
Error_Code register_task(TaskBody* task_body, const RangeType& range, int n, ...);
#endif


#ifdef REUSE_GRAPH
Error_Code dag_start(int dagNo, int *int_vec, int n1, double *float_vec, int n2);
#endif
void spawn_and_goout(void *taskptr);

#ifdef CONCURRENT_CONSTRUCT_GRAPH
void wait_for_all_task();
void acemesh_wait_for_all_task();
#endif

#ifdef THREAD_TRACE
void acemesh_trace_print(const char event_name[]);
void slave_acemesh_trace_print(const char event_name[]);
#endif

// Error_Code begin_split_task(const std::string& loop_info);
Error_Code begin_split_task_charPara(char *loop_info, void *taskptr);
Error_Code begin_split_task_zeroPara(void *taskptr);
Error_Code end_split_task_zeroPara(void *taskptr);

void spawn_and_wait_zeroPara(void *taskptr);
void spawn_and_wait(int print_graph, void *taskptr);

#if defined(AUTO_PARTITION) || defined(SUPPORT_PARTITION)
void spawn_and_wait_sep();
#endif

extern int num_registers;   

int aceMesh_get_thread_id();
void aceMesh_MPI_rank(int rank);
void acemesh_noop_touch_integer(int *data);
void acemesh_noop_touch_real(double *data);

//debug interface
void print_to_thread_file(const char* format, ... );

// template implementation.
#ifdef __ACEMESH_THREAD_GROUP
#include <cstdarg>
#include "aceMesh_hierarchy_task.h"

void do_register(aceMesh_task* p_task, int n, va_list* p_args);

template <typename TaskBody, typename RangeType>
Error_Code register_task(TaskBody *task_body, const RangeType &range, int n, ...)
{
    aceMesh_task *hierarchy_task =
        ACEMESH_NEW AceMesh_runtime::aceMesh_hierarchy_task<TaskBody, RangeType>(task_body, range);

    va_list args;           
    va_start(args,n); 
    do_register(hierarchy_task, n, &args);
    //sort(addrs.begin(), addrs.end(), )
    va_end(args);
    
    return ACEMESH_OK;
}
#endif

/* *********************************
 * Sveral interfaces for clang     *
 * Author: Han Dong Ni             *
 * Date : 2013-12-31               *
 * *********************************/

typedef void (*TASK_FUNCPTR)(volatile void *args);
#ifndef SWF
typedef void (*NEIGHBOR_FUNCPTR)(void** neighbor_addrs, int *num_neigh, void* args);
#else
  typedef void (*NEIGHBOR_FUNCPTR)(void *args);
#endif

void acemesh_runtime_init(int total_threads);
void acemesh_runtime_shutdown();
//Acemesh task generator
void acemesh_task_generator(void* taskfptr,void* args,  unsigned int args_size);
void acemesh_task_generator_with_neighbors(void *taskfptr, void *args, unsigned int args_size,
                                           void *cxx_this_pointer, void *get_neighbors_funcptr,
                                           void *neighbor_args);

void acemesh_composite_task_generator(void *taskfptr, void *args, unsigned int args_size);
void acemesh_composite_task_generator_with_neighbors(void *taskfptr, void *args,
                                                     unsigned int args_size, void *cxx_this_pointer,
                                                     void *get_neighbors_funcptr,
                                                     void *neighbor_args);

// Handler for #pragma acemesh data clause
void acemesh_push_wrlist(int argc, void *addr, int access_flag, ...);
void acemesh_push_rlist(int argc, void *addr, int access_flag, ...);
void acemesh_push_wlist(int argc, void *addr, int access_flag, ...);

#ifdef REUSE_GRAPH
int acemesh_dag_start(int dagNo);
#endif

void acemesh_begin_split_task(char *loop_info);
void acemesh_end_split_task();
void acemesh_spawn_and_wait(int print_graph);	

void *acemesh_malloc_obj(unsigned int args_size);

void acemesh_task_map_master();
void acemesh_set_suspend(int mode);
void acemesh_task_set_type(int type);

#ifdef SUPPORT_PARTITION
void acemesh_setsepid(int id);
#endif

void* aceMesh_get_data();
void aceMesh_set_data(void* data);
int get_part_id();
void set_part_id(int part_id);

#ifdef __cplusplus
}
#endif
#endif
